iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0
Kubernetes

Kubernetes GPU 工作負載工程:排程、資源隔離、推論服務與可觀測性系列 第 2

Day 02|GPU Kubernetes 參考架構:Node、Runtime、Storage 與 Model Serving

  • 分享至 

  • xImage
  •  

昨天我的結論是:Kubernetes 不會讓 GPU 變多,它真正解決的是多節點、多服務與多種資源之間的治理。

但要驗證這件事,不能一開始就拿一大包 Helm chart 往叢集裡裝。當 Pod 卡在 Pending,或者 container 裡的 nvidia-smi 失敗時,如果我分不出是 Driver、Container Toolkit、Device Plugin 還是 Scheduler 的問題,Kubernetes 只會讓故障多一層包裝。

所以今天我先做兩件事:畫出後續 28 天會共用的參考架構,然後在手邊這台機器上跑一次 preflight,看看我現在真的能驗證到哪一層。


我把「GPU Pod 能跑」拆成六層

一開始畫圖時,我最容易把 Device Plugin 和 Container Toolkit 混在一起。實際把路徑拆開後,它們處理的是不同問題:

                        Kubernetes Control Plane
                API Server ─ Scheduler ─ Controller
                     │          │
                     │       根據 Node 可用資源做 placement
                     ▼
┌────────────────────── GPU Node ───────────────────────┐
│ Kubelet                                                  │
│   ├── NVIDIA Device Plugin ──► 回報 nvidia.com/gpu       │
│   └── Container Runtime ────► NVIDIA Container Toolkit  │
│                                  │                       │
│                                  ▼                       │
│                           Driver ─ GPU / VRAM             │
│                                                          │
│ Model Serving Pod ──► Model Cache / Persistent Storage   │
│        │                                                 │
│        ├────────────► Service / Ingress ──► Client       │
│        └────────────► Metrics / Logs / Traces            │
└──────────────────────────────────────────────────────────┘

外部依賴:Container Registry、Model Registry / Object Storage

我把每一層都配上一個可觀察的現象:

層次 我想確認的問題 異常時實際會看到什麼
GPU + Driver Host 有沒有辨識到裝置 Node 上的 nvidia-smi 就失敗
Container Toolkit / Runtime Container 能不能取得 GPU Host 看得到,container 看不到
Device Plugin Kubelet 有沒有回報裝置 Node 沒有 nvidia.com/gpu capacity
Scheduler Pod 能不能放到合適節點 Pod Pending,event 顯示資源不足
Storage / Registry Image 與模型權重能不能取得 ImagePullBackOff 或模型載入失敗
Serving / Service 模型是不是真的準備好接流量 Pod Running,API 卻還不能用

這張表改變了我後續的檢查順序。如果 Host 上的 nvidia-smi 都失敗,就還沒有必要追 Scheduler;如果 Node 已經公告 nvidia.com/gpu,Pod 卻還在 Pending,才往 resource request、label、taint 與 affinity 的方向看。

Control Plane 和 GPU Worker 要先分工

我對這套測試環境的最小規劃,是一個 control-plane node 與一個 GPU worker。Control Plane 負責維持叢集 desired state;GPU worker 才放 inference、embedding、ASR 或 batch job。如果之後加入 CPU worker,ingress、controller 和一般 API 也會盡量移出 GPU node。

我不是因為 control plane 「絕對不能」跑 workload 才這麼拆,而是希望後面做資源競爭與故障注入時,不要連用來觀察和恢復叢集的控制面也一起被拖下水。

GPU worker 之後會加上型號、VRAM 級距等 label,也會用 taint 擋住一般 Pod。這些設定會在 Day 06 到 Day 09 再實際驗證;今天我只先把角色邊界留下來。


Storage 不只是「掛一個 PVC」

畫到 storage 這一段時,我原本也想用一個 PVC 簡化掉。但把復原方式列出來後,就發現三種資料根本不應該用同一種策略:

資料 我關心的特性 目前的處理方式
Container image 有版本、可以重新拉取 OCI Registry,部署時固定 digest
Model weights 很大、讀取密集、通常可重建 Model/Object Registry + Node cache
應用與使用者資料 不能任意遺失 Stateful storage + backup/restore

模型 cache 不見的代價是下次啟動變慢;資料庫不見卻可能無法復原。這個差異也會影響 readiness:container process 啟動不代表模型已下載、權重已載入、GPU 已分配且 warm-up 已完成。Running 和「可以接流量」不能畫上等號。

我準備的第一個 GPU Pod

第一個 Pod 不會先跑模型,而是只做 capability check。這樣失敗時變因比較少:

apiVersion: v1
kind: Pod
metadata:
  name: gpu-capability-check
spec:
  restartPolicy: Never
  containers:
    - name: cuda-check
      image: nvidia/cuda:12.4.1-base-ubuntu22.04
      command: ["bash", "-lc"]
      args: ["nvidia-smi"]
      resources:
        limits:
          nvidia.com/gpu: 1

這裡只寫 GPU limits 不是漏寫。Kubernetes 對 GPU extended resource 允許只寫 limits,request 會採相同值;如果 request 和 limit 都寫,兩者必須相等。

到真正的 GPU 叢集後,我會照這個順序查:

# 1. Node 是否對 Kubernetes 公告 GPU
kubectl get nodes \
  -o custom-columns='NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu'

# 2. Device Plugin 是否正常
kubectl -n kube-system get pods -l name=nvidia-device-plugin-ds

# 3. 套用 capability Pod 並查看 placement
kubectl apply -f k8s/gpu-capability-check.yaml
kubectl get pod gpu-capability-check -o wide

# 4. 從 container 裡看取得的 GPU
kubectl logs gpu-capability-check

# 5. Pending 時直接看 Scheduler 理由
kubectl describe pod gpu-capability-check

今天手邊這台機器,能驗證到哪裡?

我目前操作的是 arm64 macOS,不是 NVIDIA GPU Node,也沒有連入可用的 GPU Kubernetes 叢集。我還是先跑了 preflight,但把實驗結果記成 skipped

PYTHONPATH=src python3 scripts/record_preflight.py \
  --track kubernetes \
  --kind kubernetes-gpu-preflight \
  --experiment-id kubernetes-day02-preflight-20260916-001 \
  --require-executable kubectl \
  --require-executable nvidia-smi \
  --reason "No GPU Kubernetes context was accessed; no workload was executed."

我在終端看到的結果是:

required executables: 2
available executables: 1
kubectl: available
nvidia-smi: unsupported
status: skipped (no cluster workload was executed)

我又回到 shell 直接確認:

$ command -v kubectl
/usr/local/bin/kubectl

$ command -v nvidia-smi || echo 'nvidia-smi: not found'
nvidia-smi: not found

這個畫面其實正好驗證了前面畫的邊界:有 kubectl 只代表這台機器有 client,與 Node 有沒有 GPU、Device Plugin 有沒有回報資源、Pod 能不能被排程,都還是不同的事。

如果我只為了讓文章看起來完整,貼一段預期的 kubectl logs 就宣告成功,那後面的排程、隔離與恢復實驗也沒有可信度。

明天要補上的實機證據

Day 03 我需要回到一台可存取的 NVIDIA Linux GPU node,並且要依序看到:

  1. Host 上的 nvidia-smi 可以列出實際裝置。
  2. Container runtime 已整合 NVIDIA Container Toolkit。
  3. Device Plugin 正常,Node allocatable 出現 nvidia.com/gpu
  4. Capability Pod 排到 GPU node,log 能列出同一張 GPU。
  5. Pod 結束後,資源可以再次被排程。

這一段確實需要 GPU 雲端資源或可存取的實體節點。我會等真正跑過 kubectl getdescribelogs 和 GPU telemetry 後,再放成功畫面。

今天的結論

我今天沒有得到一個成功的 GPU Pod,但我已經知道下一次失敗時要停在哪一層找原因。

Driver 讓 Host 看見 GPU,Toolkit 讓 container 看見 GPU,Device Plugin 讓 Kubernetes 看見 GPU,Scheduler 才能決定 Pod 要去哪裡。

這就是這張參考架構對我的用途:不是證明系統很完整,而是把每一層應該出現的證據先排好。

下一篇:Day 03|把 GPU 變成 Kubernetes Resource:Driver、Toolkit 與 Device Plugin


參考資料

  1. Kubernetes Documentation, Schedule GPUs
  2. Kubernetes Documentation, Device Plugins
  3. Kubernetes Documentation, Resource Management for Pods and Containers
  4. NVIDIA Documentation, Container Toolkit

本系列以自建、可公開的測試環境驗證設計,不公開任何內部網路、帳號或未公開的實際部署資訊。


上一篇
Day 01|為什麼 AI 工作負載需要 Kubernetes?從單機實驗到可治理的 GPU 平台
下一篇
Day 03|把 GPU 變成 Kubernetes Resource:Driver、Toolkit 與 Device Plugin
系列文
Kubernetes GPU 工作負載工程:排程、資源隔離、推論服務與可觀測性10
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言